﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace SharpDB.DDTekOracle
{
    /// <summary>
    /// 管理 Timer 的 Interval  
    /// 在 Timer 的 Timer_Elapsed 事件中 InsertChangePoint（通过逻辑 得出 这一点的数据是否有变化），将 这一点变化记录下来， 
    /// 积累多次 数据变化点，来进行判断 该变 Timer 的 Interval，从而达到 减少 Timer_Elapsed 执行次数的目的，优化Timer 的作用。
    /// </summary>
    internal class IntervalMgr
    {
        public Timer ExecTimer { get; private set; }

        /// <summary>
        /// Timer默认间隔 单位（毫秒）
        /// </summary>
        public double DefInterval { get; private set; }

        /// <summary>
        /// 记录变化点的容器
        /// </summary>
        public List<bool> LstChangePoint
        {
            get; private set;
        }

        /// <summary>
        /// 每次递增的断定个数
        /// </summary>
        public int IncLastCount { get; private set; }

        /// <summary>
        /// 初始断定个数
        /// </summary>
        private int LastCount = 0;

        /// <summary>
        /// 最大执行间隔 单位（毫秒）
        /// </summary>
        public double MaxInterval { get; private set; }


        /// <param name="execTimer">间隔执行的Timer</param>
        /// <param name="defInterval">execTimer的默认间隔事件 单位（毫秒）</param>
        /// <param name="incLastCount">递增数</param>
        /// <param name="maxInterval">最大执行间隔 单位（毫秒）</param>
        public IntervalMgr(Timer execTimer, double defInterval, int incLastCount, double maxInterval = (1000 * 60 * 60))
        {
            this.ExecTimer = execTimer;
            this.DefInterval = defInterval;
            this.LastCount = incLastCount;
            this.IncLastCount = incLastCount;
            this.MaxInterval = maxInterval;
            this.LstChangePoint = new List<bool>();
        }

        /// <summary>
        /// 在 Timer_Elapsed 事件 调用 InsertChangePoint
        /// </summary>
        /// <param name="isChange">（检测数据）是否发生变化</param>
        public void InsertChangePoint(bool isChange)
        {
            LstChangePoint.Add(isChange);
           //Console.WriteLine("LastCount:" + LastCount + "\r\nLstChangePoint.Count:" + LstChangePoint.Count + "\r\nLstChangePoint:" + string.Join(",", LstChangePoint));

            if (CanJudg(LastCount))
            {
                if (JudgIsNotChange(LastCount))
                {
                    if ((ExecTimer.Interval + 1000) < MaxInterval)// 小于 最大间隔，则 继续累加 1 秒 。
                    {
                        ExecTimer.Interval = (ExecTimer.Interval + 1000);
                        LastCount = LastCount + IncLastCount;     //下次 要累加 1秒 所需的 检测变化的个数
                    }
                    else
                    {
                        ExecTimer.Interval = MaxInterval; //大于 最大间隔，则以 最大间隔 进行执行                           
                        LstChangePoint.RemoveRange(0, 1);
                    }
                }
                else
                {
                    //一旦有变化则重置。
                    ReStart();
                }
            }
        }

        /// <summary>
        /// 特殊情况下，需要执行重置方法
        /// </summary>
        public void ReStart()
        {
            LstChangePoint.Clear();
            ExecTimer.Interval = DefInterval;
            LastCount = IncLastCount;
        }

        /// <summary>
        /// 是否可以断定最后 lastCout 个变化点
        /// </summary>
        /// <param name="lastCout"></param>
        /// <returns></returns>
        private bool CanJudg(int lastCout)
        {
            if (lastCout <= 0)
            {
                return false;
            }
            return LstChangePoint.Count >= lastCout;
        }

        /// <summary>
        /// 最后 lastCout 个都没变化？
        /// </summary>
        /// <param name="lastCout"></param>
        /// <returns></returns>
        private bool JudgIsNotChange(int lastCout)
        {
            if (LstChangePoint.Count >= lastCout)
            {
                var lstTake = LstChangePoint.Skip(LstChangePoint.Count - lastCout).Take(lastCout).ToList();
                return lstTake.TrueForAll(t => !t);//全部都为false，则就是全部都没有进行过变化
            }
            else
            {
                throw new Exception("积累的变化点还不足以判断出最后" + lastCout + "个变化点是否都发生变化！");
            }
        }
    }

}
